<!doctype html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=911987
-->
<head>
  <meta charset=utf-8>
  <title>Test for CSS Animation and Transition event handler
         attributes. (Bug 911987)</title>
  <script type="application/javascript"
    src="/tests/SimpleTest/SimpleTest.js"></script>
  <script type="application/javascript" src="animation_utils.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
  <style>
  @keyframes anim  { to { margin-left: 100px } }
  </style>
</head>
<body>
<a target="_blank"
  href="https://bugzilla.mozilla.org/show_bug.cgi?id=911987">Mozilla Bug
  911987</a>
<div id="display"></div>
<pre id="test">
<script type="application/javascript">
'use strict';

// Create the div element with event handlers.
// We need two elements: one with the content attribute speficied and one
// with the IDL attribute specified since we can't set these independently.
function createAndRegisterTargets(eventAttributes) {
  var displayElement = document.getElementById('display');
  var contentAttributeElement = document.createElement("div");
  var idlAttributeElement     = document.createElement("div");
  displayElement.appendChild(contentAttributeElement);
  displayElement.appendChild(idlAttributeElement);

  // Add handlers
  eventAttributes.forEach(event => {
    contentAttributeElement.setAttribute(event, 'handleEvent(event);');
    contentAttributeElement.handlerType = 'content attribute';
    idlAttributeElement[event] = handleEvent;
    idlAttributeElement.handlerType = 'IDL attribute';
  });

  return [contentAttributeElement, idlAttributeElement];
}

function handleEvent(event) {
  if (event.target.receivedEventType) {
    ok(false, `Received ${event.type} event, but this element have previous `
              `received event '${event.target.receivedEventType}'.`);
    return;
  }
  event.target.receivedEventType = event.type;
}

function checkReceivedEvents(eventType, elements) {
  elements.forEach(element => {
    is(element.receivedEventType, eventType,
       `Expected to receive '${eventType}', got
        '${element.receivedEventType}', for event handler registered
        using ${element.handlerType}`);
    element.receivedEventType = undefined;
  });
}

// Take over the refresh driver right from the start.
advance_clock(0);

// 1. Test CSS Animation event handlers.

var targets = createAndRegisterTargets([ 'onanimationstart',
                                         'onanimationiteration',
                                         'onanimationend' ]);
targets.forEach(div => {
  div.setAttribute('style', 'animation: anim 100ms 2');
  getComputedStyle(div).animationName;  // flush
});

advance_clock(0);
checkReceivedEvents("animationstart", targets);

advance_clock(100);
checkReceivedEvents("animationiteration", targets);

advance_clock(200);
checkReceivedEvents("animationend", targets);

targets.forEach(div => { div.remove(); });

// 2a. Test CSS Transition event handlers (without transitioncancel)

var targets = createAndRegisterTargets([ 'ontransitionrun',
                                         'ontransitionstart',
                                         'ontransitionend',
                                         'ontransitioncancel' ]);
targets.forEach(div => {
  div.style.transition = 'margin-left 100ms 200ms';
  getComputedStyle(div).marginLeft; // flush
  div.style.marginLeft = "200px";
  getComputedStyle(div).marginLeft; // flush
});

advance_clock(0);
checkReceivedEvents("transitionrun", targets);

advance_clock(200);
checkReceivedEvents("transitionstart", targets);

advance_clock(100);
checkReceivedEvents("transitionend", targets);

targets.forEach(div => { div.remove(); });

// 2b. Test CSS Transition cancel event handler.

var targets = createAndRegisterTargets([ 'ontransitioncancel' ]);
targets.forEach(div => {
  div.style.transition = 'margin-left 100ms 200ms';
  getComputedStyle(div).marginLeft; // flush
  div.style.marginLeft = "200px";
  getComputedStyle(div).marginLeft; // flush
});

advance_clock(200);

targets.forEach(div => {
  div.style.display = "none"
});
getComputedStyle(targets[0]).display; // flush

advance_clock(0);
checkReceivedEvents("transitioncancel", targets);

advance_clock(100);
targets.forEach( div => { is(div.receivedEventType, undefined); });

targets.forEach(div => { div.remove(); });

// 3. Test prefixed CSS Animation event handlers.

var targets = createAndRegisterTargets([ 'onwebkitanimationstart',
                                         'onwebkitanimationiteration',
                                         'onwebkitanimationend' ]);
targets.forEach(div => {
  div.setAttribute('style', 'animation: anim 100ms 2');
  getComputedStyle(div).animationName;  // flush
});

advance_clock(0);
checkReceivedEvents("webkitAnimationStart", targets);

advance_clock(100);
checkReceivedEvents("webkitAnimationIteration", targets);

advance_clock(200);
checkReceivedEvents("webkitAnimationEnd", targets);

targets.forEach(div => { div.remove(); });

// 4. Test prefixed CSS Transition event handlers.

advance_clock(0);
var targets = createAndRegisterTargets([ 'onwebkittransitionend' ]);
targets.forEach(div => {
  div.style.transition = 'margin-left 100ms';
  getComputedStyle(div).marginLeft; // flush
  div.style.marginLeft = "200px";
  getComputedStyle(div).marginLeft; // flush
});

advance_clock(100);
checkReceivedEvents("webkitTransitionEnd", targets);

targets.forEach(div => { div.remove(); });

SpecialPowers.DOMWindowUtils.restoreNormalRefresh();

</script>
</body>
</html>
